package proxyskeleton;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;

import jaxb.messages.*;

import org.ietf.jgss.GSSContext;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.MessageProp;

import encryption.EncryptionManager;

import virtualHD.*;

public class VirtualHDProxy implements VirtualHD{

	private String hostname;
	private int port;
	private String serviceName;
	
	private String[] users;

	private EncryptionManager encManager = null;
	private Socket socket;
	private DataInputStream inStream;
	private DataOutputStream outStream;


	public VirtualHDProxy(String serverAddress, int port,String serviceName) {
		this.hostname = serverAddress;
		this.port = port;
		this.serviceName = serviceName;	
	}


	@Override
	public void openHD(String userid, String password) throws VirtualHDException {
		LoginContext lc = null;

		try {
			lc = new LoginContext("VirtualHDClient", new ClientCallbackHandler(userid,password));
			lc.login();

			Subject sub = lc.getSubject();

			socket = new Socket(hostname, port);

			GSSContext context = Subject.doAs(sub, new CreateGSSContextAction(socket,serviceName));
			MessageProp prop =  new MessageProp(0, true);
			encManager = new EncryptionManager(context,prop,0);

			inStream = new DataInputStream(socket.getInputStream());
			outStream = new DataOutputStream(socket.getOutputStream());
			
			//now server will send a message containing a list of resources
			//in this case each resource is a folder's name, so we'll get a list of
			//strongbox! users
			Message userMess = new Message(new String(encManager.readEncMessage(inStream)));
			List<Resource> userList = userMess.getResource();
			users = new String[userList.size()];
			for(int i=0;i<users.length;i++){
				users[i] = new String(userList.get(i).getName());
			}
			
		} catch (LoginException e) {
			throw new VirtualHDException(e.getMessage());
		} catch (IOException e){
			throw new VirtualHDException(e.getMessage());
		} catch (GSSException e) {
			throw new VirtualHDException(e.getMessage());
		}

	}

	@Override
	public Resource[] view(Folder f) throws VirtualHDException {
		Message request = new Message();
		Resource[] resourcelist = null;
		request.setType(Message.VIEW);
		request.setFolder(f);


		try {
			encManager.writeEncMessage(request.toXMLString().getBytes(),outStream);
			String reply = null;
			reply=new String(encManager.readEncMessage(inStream));
			Message response=new Message(reply);
			if (response.getType().equals(Message.ERROR))
				throw new VirtualHDException(response.getCause());
			else{
				List<Resource> list = response.getResource();
				resourcelist = new Resource[list.size()];
				for(int i=0;i<resourcelist.length;i++)
					resourcelist[i] = list.get(i);
			}
		} catch (GSSException e) {
			throw new VirtualHDException(e.getMessage());
		} catch (IOException e) {
			throw new VirtualHDException(e.getMessage());
		}

		return resourcelist;
	}

	@Override
	public void put(Folder f, Resource r) throws VirtualHDException {
		File resourceFile = new File(r.getName()); //r is in the form of absolute path
		r.setName(resourceFile.getName()); //now we have just the name
		
		Message request = new Message();
		request.setType(Message.PUT);
		request.setFolder(f);
		ArrayList<Resource> resourcelist = new ArrayList<Resource>();
		resourcelist.add(r);
		request.setResource(resourcelist);
		try{

			encManager.writeEncMessage(request.toXMLString().getBytes(),outStream);
			String reply = null;

			reply=new String(encManager.readEncMessage(inStream));
			Message response=new Message(reply);
			if (response.getType().equals(Message.ERROR))
				throw new VirtualHDException(response.getCause());
			else{
				int port2 = response.getPort();

				Socket socket = new Socket(hostname, port2);
				DataOutputStream socketOutStream = new DataOutputStream(socket.getOutputStream());
				DataInputStream fileInStream = new DataInputStream(new FileInputStream(resourceFile));
				encManager.secureSend(fileInStream,socketOutStream);

				socket.close();
				socketOutStream.close();
				fileInStream.close();
			}
		} catch (GSSException e) {
			throw new VirtualHDException(e.getMessage());
		} catch (IOException e) {
			throw new VirtualHDException(e.getMessage());
		}
	}

	@Override
	public void get(Folder f, Resource r) throws VirtualHDException {
		File resourceFile = new File(r.getName());
		r.setName(resourceFile.getName());
		
		Message request = new Message();
		request.setType(Message.GET);
		request.setFolder(f);
		ArrayList<Resource> resourcelist = new ArrayList<Resource>();
		resourcelist.add(r);
		request.setResource(resourcelist);
		try{

			encManager.writeEncMessage(request.toXMLString().getBytes(),outStream);
			String reply = null;
			reply=new String(encManager.readEncMessage(inStream));

			Message response=new Message(reply);
			if (response.getType().equals(Message.ERROR))
				throw new VirtualHDException(response.getCause());
			else{
				int port2 = response.getPort();

				Socket socket = new Socket(hostname, port2);
				DataInputStream socketInStream = new DataInputStream(socket.getInputStream());
				DataOutputStream fileOutStream = new DataOutputStream(new FileOutputStream(resourceFile));

				encManager.secureReceive(socketInStream,fileOutStream);

				socket.close();
				fileOutStream.close();
				socketInStream.close();

			}

		} catch (GSSException e) {
			throw new VirtualHDException(e.getMessage());
		} catch (IOException e) {
			throw new VirtualHDException(e.getMessage());
		}
	}

	@Override
	public void del(Folder f, Resource r) throws VirtualHDException {
		Message request = new Message();
		request.setType(Message.DEL);
		request.setFolder(f);
		ArrayList<Resource> resourcelist = new ArrayList<Resource>();
		resourcelist.add(r);
		request.setResource(resourcelist);
		try{

			encManager.writeEncMessage(request.toXMLString().getBytes(),outStream);

			String reply = null;

			reply=new String(encManager.readEncMessage(inStream));


			Message response=new Message(reply);
			if (response.getType().equals(Message.ERROR))
				throw new VirtualHDException(response.getCause());

		} catch (GSSException e) {
			throw new VirtualHDException(e.getMessage());
		} catch (IOException e) {
			throw new VirtualHDException(e.getMessage());
		}

	}

	@Override
	public void change_role(Folder folder) throws VirtualHDException{
		Message request = new Message();
		request.setType(Message.CHANGE_ROLE);
		request.setFolder(folder);
		try{
			encManager.writeEncMessage(request.toXMLString().getBytes(), outStream);

		} catch (GSSException e) {
			throw new VirtualHDException(e.getMessage());
		} catch (IOException e) {
			throw new VirtualHDException(e.getMessage());
		}
	}
	
	@Override
	public String[] getUsers() throws VirtualHDException {
		if(users == null)
			throw new VirtualHDException("You have to make a login first!");	
		return users;
	}

	@Override
	public void closeHD() throws VirtualHDException {
		Message request = new Message();
		request.setType(Message.CLOSE);
		try{
			encManager.writeEncMessage(request.toXMLString().getBytes(),outStream);
			inStream.close();
			outStream.close();
			socket.close();
		} catch (GSSException e) {
			throw new VirtualHDException(e.getMessage());
		} catch (IOException e) {
			throw new VirtualHDException(e.getMessage());
		}
	}

}
